{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "54bd32c5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-09T06:52:38.038063Z",
     "start_time": "2023-12-09T06:52:37.104554Z"
    },
    "execution": {
     "iopub.execute_input": "2024-02-22T09:26:17.229515Z",
     "iopub.status.busy": "2024-02-22T09:26:17.228518Z",
     "iopub.status.idle": "2024-02-22T09:26:17.261430Z",
     "shell.execute_reply": "2024-02-22T09:26:17.261430Z",
     "shell.execute_reply.started": "2024-02-22T09:26:17.229515Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import scipy.fft as fft\n",
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "from tqdm import tqdm\n",
    "import pandas as pd\n",
    "from cv2 import dct\n",
    "import imageio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "2f270776",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-09T07:35:19.444806Z",
     "start_time": "2023-12-09T07:35:19.430842Z"
    },
    "execution": {
     "iopub.execute_input": "2024-02-22T09:26:17.943213Z",
     "iopub.status.busy": "2024-02-22T09:26:17.942215Z",
     "iopub.status.idle": "2024-02-22T09:26:17.980115Z",
     "shell.execute_reply": "2024-02-22T09:26:17.979115Z",
     "shell.execute_reply.started": "2024-02-22T09:26:17.943213Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [],
   "source": [
    "def image_hash(img_path):\n",
    "    img = processing(img_path)\n",
    "    C_r_list = image_feature(img)\n",
    "    h_i = gen_hashing(C_r_list)\n",
    "    return h_i\n",
    "\n",
    "def processing(img_path):\n",
    "    \"\"\"\n",
    "    input：图片的路径\n",
    "    output：处理后的RGB图片\n",
    "    \"\"\"\n",
    "    try:\n",
    "        img = cv2.imread(img_path)\n",
    "        img = cv2.resize(img, (256,256), interpolation=cv2.INTER_LINEAR)\n",
    "    except:\n",
    "        img = imageio.mimread(img_path)\n",
    "        img = np.array(img)\n",
    "        img = img[0]\n",
    "        img = img[:, :, 0:3]\n",
    "        img = cv2.resize(img, (256,256), interpolation=cv2.INTER_LINEAR)\n",
    "    # img = cv2.imread(img_path)\n",
    "    # img = cv2.resize(img, (256,256), interpolation=cv2.INTER_LINEAR)\n",
    "#     out = cv2.GaussianBlur(img, (3, 3),1.3) # 使用python自带的高斯滤波\n",
    "    kernel = np.array([[1,2,1],[2,4,2],[1,2,1]])/16\n",
    "    out = cv2.filter2D(img, -1 , kernel=kernel)  # 二维滤波器\n",
    "    out = cv2.cvtColor(out, cv2.COLOR_BGR2RGB)\n",
    "    out = square2circle(out)\n",
    "    log_polar_img = cv2.logPolar(out, (out.shape[1] // 2, out.shape[0] // 2), 50, cv2.WARP_FILL_OUTLIERS)\n",
    "    return log_polar_img\n",
    "\n",
    "def square2circle(img):\n",
    "    \"\"\"\n",
    "    将最大内切圆以外的像素值置为0\n",
    "    img:array(512,512,3)\n",
    "    return:array(512,512,3)\n",
    "    \"\"\"\n",
    "    h,w,_ = img.shape\n",
    "    radius = int(min(h, w) / 2)  \n",
    "    mask = np.zeros((h, w,3), np.uint8)  \n",
    "    # 在mask中画出一个最大半径为radius的圆形区域\n",
    "    cv2.circle(mask, (w // 2, h // 2), radius, (255, 255, 255), -1)  \n",
    "    # 对原图和mask进行位运算，将圆形区域外的像素设置为0  \n",
    "    img_process = cv2.bitwise_and(img, mask)\n",
    "    return img_process\n",
    "\n",
    "def image_feature(img):\n",
    "    \"\"\"\n",
    "    iamge:(512,512,3)\n",
    "    return: array格式(x,64,64)\n",
    "    \"\"\"\n",
    "    C_r_list = np.zeros((0,64,64)).tolist()\n",
    "    for i in range(0,img.shape[0],64):\n",
    "        for j in range(0,img.shape[0],64):\n",
    "            image_block = img[i:i+64,j:j+64,:]\n",
    "            C_r,C_i,C_j,C_k = QDCT(image_block) # 可以在这里取出实部和三个虚数的实部\n",
    "            C_r_list.append(np.sqrt(C_r**2+C_i**2+C_j**2+C_k**2).tolist())\n",
    "            # C_r_list.append(C_r.tolist())\n",
    "    return np.array(C_r_list)\n",
    "\n",
    "def gen_hashing(feature_matrix):\n",
    "    \"\"\"\n",
    "    生成图像哈希值,和原论文不同，我的P和Q矩阵是每一行代表一个图像块。\n",
    "    input:array (x,64,64)\n",
    "    output:list (x)\n",
    "    \"\"\"\n",
    "    d_i = []\n",
    "    h_i = []\n",
    "    P_matrix = np.zeros((0,32)).tolist()\n",
    "    Q_matrix = np.zeros((0,32)).tolist()\n",
    "    for i in feature_matrix:\n",
    "        i = np.array(i)\n",
    "        row = i[0,1:33].reshape(1,-1)\n",
    "        column = i[1:33,0].reshape(1,-1)\n",
    "        P_matrix.extend(row.tolist())\n",
    "        Q_matrix.extend(column.tolist())\n",
    "    P_matrix = np.array(P_matrix)\n",
    "    Q_matrix = np.array(Q_matrix)\n",
    "    # P_matrix_1 = P_matrix\n",
    "    # Q_matrix_1 = Q_matrix\n",
    "    P_matrix_1 = (P_matrix - np.mean(P_matrix,axis = 0))/np.std(P_matrix,axis = 0,ddof=1)\n",
    "    Q_matrix_1 = (Q_matrix - np.mean(Q_matrix,axis = 0))/np.std(Q_matrix,axis = 0,ddof=1)\n",
    "    d_i = np.sqrt(np.sum((P_matrix_1 - Q_matrix_1)**2,axis = 1))\n",
    "    median = np.median(d_i)\n",
    "    for i in d_i:\n",
    "        if i < median:\n",
    "            h_i.append(0)\n",
    "        else:\n",
    "            h_i.append(1)\n",
    "    return np.array(h_i)\n",
    "\n",
    "def QDCT(img):\n",
    "    \"\"\"\n",
    "    img：(64,64,3)\n",
    "    \"\"\"\n",
    "    # C_r = DCT(img[:,:,0]+img[:,:,1]+img[:,:,2]) * (- 1 / np.sqrt(3))\n",
    "    C_r = cv2.dct(np.float32(img[:,:,0]+img[:,:,1]+img[:,:,2]) * (- 1 / np.sqrt(3)))\n",
    "    C_i = cv2.dct(np.float32(img[:,:,2]-img[:,:,1]) * (1 / np.sqrt(3)))\n",
    "    C_j = cv2.dct(np.float32(img[:,:,0]-img[:,:,2]) * (1 / np.sqrt(3)))\n",
    "    C_k = cv2.dct(np.float32(img[:,:,1]-img[:,:,0]) * (1 / np.sqrt(3)))\n",
    "    # C_i = DCT(img[:,:,2]-img[:,:,1]) * (1 / np.sqrt(3))\n",
    "    # C_j = DCT(img[:,:,0]-img[:,:,2]) * (1 / np.sqrt(3))\n",
    "    # C_k = DCT(img[:,:,1]-img[:,:,0]) * (1 / np.sqrt(3))\n",
    "    return C_r,C_i,C_j,C_k\n",
    "\n",
    "def DCT(X):\n",
    "    def gen_Matrix(N):\n",
    "        Matrix = np.zeros((N,N),dtype=float)\n",
    "        for row in range(N):#遍历行\n",
    "            if row == 0:#第一行\n",
    "                for col in range(N):\n",
    "                    Matrix[row,col] = 1 / np.sqrt(N)\n",
    "            else:\n",
    "                for col in range(N):\n",
    "                    Matrix[row,col] = np.sqrt(2/N) * (np.cos( (np.pi * (2*col+1) * row) / (2*N)))\n",
    "        return Matrix\n",
    "    Size = list(X.shape)\n",
    "    Rows,Cols = Size[0],Size[1]\n",
    "    A = gen_Matrix(Rows)\n",
    "    B = gen_Matrix(Cols)\n",
    "    B = B.T\n",
    "    T = np.dot(np.dot(A,X),B)\n",
    "    return T\n",
    "    \n",
    "def dist_img(h1,h2):\n",
    "    return sum(np.abs(h1-h2))\n",
    "\n",
    "def dis_different_dir(path,des_path):\n",
    "    # 目录下图片之间的距离\n",
    "    dirs = os.listdir(path)\n",
    "    image_set_list = []\n",
    "    image_hashing_value_set = []\n",
    "    for i in tqdm(dirs,ncols = 50):\n",
    "    # for i in dirs:\n",
    "        image_hashing_value_set.append(image_hash(os.path.join(path, i)))\n",
    "    for i in range(len(image_hashing_value_set)):\n",
    "        for j in range(i+1,len(image_hashing_value_set)):\n",
    "            image_set_list.append(dist_img(image_hashing_value_set[i],image_hashing_value_set[j]))\n",
    "    for i in range(int(np.ceil(len(image_set_list)/1000000))-1):\n",
    "        start = i*1000000\n",
    "        end = (i+1)*1000000\n",
    "        pd.DataFrame(image_set_list[start:end]).to_excel(os.path.join(des_path,f\"different_image_result_{i+1}.xlsx\"),index = False)\n",
    "    pd.DataFrame(image_set_list[end+1:]).to_excel(os.path.join(des_path,f\"different_image_result_{i+2}.xlsx\"),index = False)\n",
    "    return image_set_list,image_hashing_value_set\n",
    "\n",
    "def dis_similar_dir(path):\n",
    "    \"\"\"\n",
    "    path:相同图片每种操作的目录\n",
    "    n:环数\n",
    "    block_size:分区大小\n",
    "    return:所有相同图片哈希距离的列表\n",
    "    \"\"\"\n",
    "    # 计算相同类型图片的距离\n",
    "    dir_rotation = os.listdir(path)\n",
    "    dis_similar = []\n",
    "    for i in dir_rotation:\n",
    "        if (os.path.splitext(i)[1] == \".bmp\") | (os.path.splitext(i)[1] == \".jpg\"):\n",
    "            h1 = image_hash(os.path.join(path, i))\n",
    "            dir_temp = os.path.join(path,os.path.splitext(i)[0])\n",
    "            for j in os.listdir(dir_temp):\n",
    "                h2 = image_hash(os.path.join(dir_temp,j))\n",
    "                dis_similar.append(dist_img(h1,h2))\n",
    "    return dis_similar\n",
    "\n",
    "def dir_dis_similar_dir(total_path,des_path):\n",
    "    \"\"\"\n",
    "    total_path:相同图片各种操作的根目录\n",
    "    des_path:存放结果的目录\n",
    "    return：存放每一种操作相同图片哈希距离的字典\n",
    "    \"\"\"\n",
    "    fold_each_attack = os.listdir(total_path)\n",
    "    dir_each_attack= {}\n",
    "    if not os.path.exists(os.path.join(des_path,\"similar_image_result\")):\n",
    "        os.makedirs(os.path.join(des_path,\"similar_image_result\"))\n",
    "    # total_path = os.path.join(total_path,\"similar_image_result\")\n",
    "    for i in tqdm(fold_each_attack,ncols = 50):\n",
    "        attack_path = os.path.join(total_path,i)\n",
    "        dir_each_attack[i] = dis_similar_dir(attack_path)\n",
    "        file_name = i + \".xlsx\"\n",
    "        pd.DataFrame(dir_each_attack[i]).to_excel(os.path.join(des_path,\"similar_image_result\",file_name),index = False)\n",
    "    return dir_each_attack\n",
    "\n",
    "def my_auc(image_similar, image_different):\n",
    "    from scipy import integrate\n",
    "    sum1 = len(image_similar)\n",
    "    sum2 = len(image_different)\n",
    "    tpr = []\n",
    "    fpr = []\n",
    "    threshold_max = (max(max(image_similar), max(image_different)))\n",
    "    for threshold in np.linspace(0,threshold_max,50):\n",
    "        tpr_number = len(image_similar[image_similar <= threshold])\n",
    "        fpr_number = len(image_different[image_different <= threshold])\n",
    "        tpr.append(tpr_number/sum1)\n",
    "        fpr.append(fpr_number/sum2)\n",
    "    return integrate.trapezoid(tpr,fpr)\n",
    "\n",
    "def stat_analysis(result_path):\n",
    "    \"\"\"\n",
    "    result_path:保存结果的路径\n",
    "    \"\"\"\n",
    "    dirs = os.listdir(result_path)\n",
    "    for i in dirs:\n",
    "        file_path = os.path.join(result_path, i)\n",
    "        if os.path.isdir (file_path):\n",
    "            for j in os.listdir(file_path):\n",
    "                xlsx_path = os.path.join(file_path,j)\n",
    "                data = pd.read_excel(xlsx_path,header = 0)[0]\n",
    "                print(f\"{j}的最小值：{min(data)}最大值：{max(data)}平均值：{np.mean(data)}\")\n",
    "        else:\n",
    "            xlsx_path = file_path\n",
    "            data = pd.read_excel(xlsx_path,header = 0)[0]\n",
    "            print(f\"{i}的最小值：{min(data)}最大值：{max(data)}平均值：{np.mean(data)}\")\n",
    "\n",
    "def auc_analysis(dir_path):\n",
    "    \"\"\"\n",
    "    dir_path:保存结果的路径\n",
    "    \"\"\"\n",
    "    similar_image_result_path = os.path.join(dir_path,\"similar_image_result\")\n",
    "    data_different =pd.Series()\n",
    "    for i in os.listdir(dir_path):\n",
    "        if not os.path.isdir(os.path.join(dir_path,i)):\n",
    "            print(os.path.join(dir_path,i))\n",
    "            temp = pd.read_excel(os.path.join(dir_path,i),header = 0)[0]\n",
    "            data_different = pd.concat((data_different,temp),axis=0)\n",
    "    similar_image_result_path_total = pd.Series()\n",
    "    for i in os.listdir(similar_image_result_path):\n",
    "        data_similar = pd.read_excel(os.path.join(similar_image_result_path,i),header = 0)[0]\n",
    "        similar_image_result_path_total= pd.concat((similar_image_result_path_total,data_similar),axis=0)\n",
    "        print(f\"{i}和不同图片的auc值：{my_auc(data_similar,data_different)}\")\n",
    "    print(f\"所有相同图片和不同图片 的auc值：{my_auc(similar_image_result_path_total,data_different)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "280d08c5",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-12-06T03:06:29.525926Z",
     "start_time": "2023-12-06T03:06:29.514955Z"
    },
    "execution": {
     "iopub.execute_input": "2024-02-22T09:26:22.086310Z",
     "iopub.status.busy": "2024-02-22T09:26:22.085314Z",
     "iopub.status.idle": "2024-02-22T09:33:04.550137Z",
     "shell.execute_reply": "2024-02-22T09:33:04.549139Z",
     "shell.execute_reply.started": "2024-02-22T09:26:22.086310Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|█████████| 3307/3307 [00:42<00:00, 78.73it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "different_image_result_1.xlsx的最小值：0最大值：14平均值：3.556744\n",
      "different_image_result_2.xlsx的最小值：0最大值：14平均值：3.830366\n",
      "different_image_result_3.xlsx的最小值：0最大值：14平均值：3.844634\n",
      "different_image_result_4.xlsx的最小值：0最大值：14平均值：3.693444\n",
      "different_image_result_5.xlsx的最小值：0最大值：14平均值：3.774724\n",
      "different_image_result_6.xlsx的最小值：0最大值：12平均值：3.665153171693785\n",
      "brightness.xlsx的最小值：0最大值：6平均值：1.6583333333333334\n",
      "contrast.xlsx的最小值：0最大值：4平均值：1.3\n",
      "gamma correction.xlsx的最小值：0最大值：6平均值：1.8564814814814814\n",
      "gaussian.xlsx的最小值：2最大值：6平均值：3.25\n",
      "jpeg.xlsx的最小值：0最大值：8平均值：2.6166666666666667\n",
      "rotation.xlsx的最小值：0最大值：8平均值：2.171875\n",
      "salt_pepper.xlsx的最小值：0最大值：8平均值：3.1893939393939394\n",
      "speckle.xlsx的最小值：2最大值：6平均值：3.25\n",
      "watermark.xlsx的最小值：0最大值：6平均值：0.775\n",
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_1.xlsx\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\10047\\AppData\\Local\\Temp\\ipykernel_10820\\3756971163.py:223: FutureWarning: The behavior of array concatenation with empty entries is deprecated. In a future version, this will no longer exclude empty items when determining the result dtype. To retain the old behavior, exclude the empty entries before the concat operation.\n",
      "  data_different = pd.concat((data_different,temp),axis=0)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_2.xlsx\n",
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_3.xlsx\n",
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_4.xlsx\n",
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_5.xlsx\n",
      "./新数据集结果/纯四元数版本_对数极坐标结果/different_image_result_6.xlsx\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\10047\\AppData\\Local\\Temp\\ipykernel_10820\\3756971163.py:227: FutureWarning: The behavior of array concatenation with empty entries is deprecated. In a future version, this will no longer exclude empty items when determining the result dtype. To retain the old behavior, exclude the empty entries before the concat operation.\n",
      "  similar_image_result_path_total= pd.concat((similar_image_result_path_total,data_similar),axis=0)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "brightness.xlsx和不同图片的auc值：0.779934290318981\n",
      "contrast.xlsx和不同图片的auc值：0.8165540090314225\n",
      "gamma correction.xlsx和不同图片的auc值：0.7524459757410649\n",
      "gaussian.xlsx和不同图片的auc值：0.5777105586420488\n",
      "jpeg.xlsx和不同图片的auc值：0.654097630265357\n",
      "rotation.xlsx和不同图片的auc值：0.7046840740478622\n",
      "salt_pepper.xlsx和不同图片的auc值：0.5774451510201226\n",
      "speckle.xlsx和不同图片的auc值：0.5777105586420488\n",
      "watermark.xlsx和不同图片的auc值：0.8696886625799343\n",
      "所有相同图片和不同图片 的auc值：0.7077911257666478\n"
     ]
    }
   ],
   "source": [
    "# dir_dis_similar_dir(\"../new_data/similar_image_4/\",\"./新数据集结果/纯四元数版本_对数极坐标结果\")\n",
    "dis_different_dir(\"../new_data/different_image/\",\"./新数据集结果/纯四元数版本_对数极坐标结果\")\n",
    "stat_analysis(\"./新数据集结果/纯四元数版本_对数极坐标结果/\")\n",
    "auc_analysis(\"./新数据集结果/纯四元数版本_对数极坐标结果/\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "f1f6a015-f8a5-494a-bbf2-d07d1389227c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-01-04T13:27:52.875387Z",
     "iopub.status.busy": "2024-01-04T13:27:52.873937Z",
     "iopub.status.idle": "2024-01-04T13:27:52.983561Z",
     "shell.execute_reply": "2024-01-04T13:27:52.982288Z",
     "shell.execute_reply.started": "2024-01-04T13:27:52.875304Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9383152428810719\n"
     ]
    }
   ],
   "source": [
    "# data_similar = pd.read_excel(\"./纯四元数_对数极坐标结果/image_similar_distance_90_标准化后.xlsx\",header = 0)[0]\n",
    "# data_different = pd.read_excel(\"./纯四元数结果/image_different_distance_200_标准化后.xlsx\",header=0)[0]\n",
    "auc,tpr,fpr = my_auc(data_similar_rotation_90,data_different_set_200)\n",
    "print(auc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "14c8a3dd-4b8a-4ded-a0de-6752e93a1748",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-01-27T13:28:04.164983Z",
     "iopub.status.busy": "2024-01-27T13:28:04.164983Z",
     "iopub.status.idle": "2024-01-27T13:28:04.184931Z",
     "shell.execute_reply": "2024-01-27T13:28:04.183933Z",
     "shell.execute_reply.started": "2024-01-27T13:28:04.164983Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"abc\".find(\"d\") != -1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "5ed33d2b-7fd9-4a66-aa4a-dab1b5da2ecf",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-01-04T13:45:51.825952Z",
     "iopub.status.busy": "2024-01-04T13:45:51.824408Z",
     "iopub.status.idle": "2024-01-04T13:45:52.722733Z",
     "shell.execute_reply": "2024-01-04T13:45:52.721782Z",
     "shell.execute_reply.started": "2024-01-04T13:45:51.825859Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAyXElEQVR4nO3de1xU5b7H8e9wGzG5GYI30tKtaN7KisikbJN2ozr7tLP0eKu8lF2UbrqtzC5a7Y7ZSdM0K3fb0u67s/VYhpmalIVyjrtQMyzNAtSUIVAGmHX+KCZHUBhk5mGYz/v1mhczi/XM/GbFq/X1eZ71LJtlWZYAAAAMCTFdAAAACG6EEQAAYBRhBAAAGEUYAQAARhFGAACAUYQRAABgFGEEAAAYRRgBAABGhZkuoD5cLpd+/PFHRUVFyWazmS4HAADUg2VZKikpUfv27RUScvz+j4AIIz/++KOSkpJMlwEAABpgz5496tix43F/HxBhJCoqStKvXyY6OtpwNQAAoD4cDoeSkpLc5/HjCYgwUj00Ex0dTRgBACDA1DXFggmsAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCivw8i6deuUkZGh9u3by2az6b333quzzdq1a3X22WfLbrera9eueuWVVxpQKgAAaI68DiOlpaXq27ev5s2bV6/9d+3apSuvvFKDBg1Sbm6uJk2apFtuuUUffPCB18UCAIDmx+t701x++eW6/PLL673/ggULdPrpp+s///M/JUk9evTQhg0b9Mwzz2jIkCHefjwAAGhmfH6jvOzsbKWnp3tsGzJkiCZNmnTcNuXl5SovL3e/djgcvioPABCEduzYoe+//16HDh3SoUOHVFxcXOvzwYMH68EHH/Ro27dvXxUUFNT5Gc8884yGDRvmfp2Xl6eLL764XvXl5uaqXbt27tfPP/+8ZsyYUWe75ORkffLJJx7b/vznP2vdunUnbLd27Vr16NGjXrX5gs/DSEFBgRITEz22JSYmyuFw6PDhw4qMjKzRZtasWfU66ACA4GBZlizLUkjI77MLdu/erfXr19cIEccGi4iICP3f//2fx/s98sgjWrp0aZ2f26FDhxrb9u/fr6KiojrbHjlyxON1VVVVvdpJksvl8nhdVlZWr7Zt2rSpse3QoUN1tq2srKxXXb7i8zDSEFOnTlVmZqb7tcPhUFJSksGKAADVLMuqcUv4wsJClZSUyOl0nvDRtWtX9evXz93O5XLpqaee0uHDh4/bO1FcXKzi4mJ9+umnOv/8891tN23apP/4j/+os1673V5jW2xsbL2+a2lpaY1tp512Wq3veayoqCiP1+Hh4Tr99NPr9bmhoaEer2NiYurVtmPHjjW2tW3bts624eHh9arLV3weRtq2bavCwkKPbYWFhYqOjq61V0T69Q+nPv+hAaC5Kioq0pEjR1RRUeE+kR/9vPp1r1691KlTJ3e7AwcO6NVXX62xb22P5557Tq1bt3a3ff311zVnzpxa9y0vL3c/79atm/71r3951DtixAitXr26zu81efJkjzBis9k0derUeh2T4uJij9cxMTF1tomOjlZsbKycTqciIiLc2y+77DLFxcUpNjZWMTExHj+rn8fExNR6LsrOzq5Xvcfq3r278vPzG9R27NixGjt2bIPavvrqqw1q508+DyOpqalauXKlx7bVq1crNTXV1x8NAJJ+/df3d9995z5B13aCr/558cUXKz4+3t32m2++0XvvvVdrIDj6eWhoaI1lC5588km9/fbbJ/y8iooKZWRkaPny5R5tzznnHO3Zs6fO7/bCCy9o3Lhx7tf79u3T5MmT63VcHn/8cY8wsm/fPm3atKnOdk6ns8a2o0/03rS12WwKDw9XRUVFjX3DwsIUFxfnDgnHBoOePXvq2Wef9QgQR/+Mioqq0cNQ7aqrrtJVV11Vr5rhe16HkV9++UU7d+50v961a5dyc3PVunVrnXbaaZo6dar27t2rv/3tb5KkCRMmaO7cubrvvvt00003ac2aNXrjjTe0YsWKxvsWAILCL7/8oqKiouM+9u3bp6KiIk2YMEG33nqru115ebm6dOlSr8/45JNPlJaW5n791Vdf6b777quznd1urxFGvv/+e33xxRd1tj18+HCNbQ09ude33Yna2u12RUREHPdx2mmn1Xiviy++WHFxcSdsFxERobPOOqtG27ffflthYWHuUFEdKCIjI2sMBx2tQ4cOuvPOO+v9fdF0eR1GvvzySw0aNMj9unpux6hRo/TKK6/op59+0u7du92/P/3007VixQpNnjxZzz77rDp27KgXX3yRy3oBqLy83B0gqn9WP6KjozVt2jSP/YcMGaKNGzfW+b7HdoV7Mx7e0JO70+msMZciPDxcNpvNfSIODw/3+Fn9/OhhlmqXXXaZioqKauxf/To8PFx2u10pKSke7RITE/X666/XGQoiIiJqzC8YP368xo8ff8IAcDz33HOP122qZWRkNLgtmgebZVmW6SLq4nA4FBMTo+LiYkVHR5suB8BxVFVV6cCBAx6hIiMjQ6eccop7n6VLl2rGjBkqKiqqMQfgaF27dtU333zjse3aa6/VP/7xjxPWYLfbNX78eD377LPubZZlafjw4bUGgmN/XnfddercubO7bUFBgTZu3FgjQNQWMDp16uRxIne5XB5XfwDBpr7n7yZ5NQ2ApsGyLDkcDtlsNo//kfz888+aPn16jeGR/fv369h/33z11Vfq2bOn+3VFRUWNkFGb2i5FTEtLU2RkpBISEjwebdq0cT+Pioqq8S97m82m1157zduvL+nXSfh/+tOfGtSWIALUD2EECGLr1q1Tfn7+CedhVFRUaObMmR5XPNhsNs2dO7den1FUVOQRRhISEhQbG1sjRNQWMI519CX/AJoPwggQBMrKyvTDDz+oW7duHtsfeOABrV+/vs72x/ZSxMbGKiwszL1QUosWLZSYmFhrb8XRQx6SdMUVV+jgwYMn94UANCuEEaAZOnjwoD799FOtX79e69at05dffqnk5GRt3brVY7+EhIRa24eGhnr0WnTt2tXj9zabTZ999pni4uKUkJCgU045pUGTHgFAYgIr0CwUFBS4g8e6deu0devWGnM3pF+XsT711FPdr1esWKHvv/++xhBJbGws8x0AnDQmsAJBYv369R7rYtQmOTlZaWlpNdazuPLKK31ZGgDUC2EEaOJcLpfy8vLcPR8ZGRm68cYb3b8/66yzFBoaqqqqKkm/XsHRr18/paWlaeDAgbrwwguPOxwDAE0BYQRoYiorK5Wbm6t169Zp/fr1Wr9+vQ4cOOD+vc1m8wgjrVq10k033aT4+HilpaXpggsuYDgTQEAhjABNxFtvvaVFixZp48aN+uWXX467X05OTo1tCxcu9GVpAOBThBHAzxwOhzZu3KhLLrnEY6nx3bt368MPP6yxf+vWrXXhhRcqLS1NaWlpHnc8BYDmgDAC+Ni+ffu0YcMG95Uuubm5crlc2rhxo8fdqwcOHChJat++vXu+R1pamnr27MmVLQCaNcII0Mh2797tnmy6fv165eXl1brfunXrPMLIWWedpZ07d+qMM85gzQ4AQYUwApyEioqKGneEvfjii7Vr167jtundu7fS0tI8gogkhYWF1fs29wDQnBBGgDpYlqWioiJt27ZN27ZtU15envt5SEhIjdvVp6WlucNIaGiozjnnHPeQy4ABA9S6dWsTXwMAmizCCFCLf/3rX5o9e7Y7eBw6dOi4+5aVlally5bu10OHDlWnTp00cOBAnX/++WrVqpUfKgaAwEUYQdApKSnR9u3bPXo5Jk2a5J5AKkmlpaV6+eWXT/g+MTEx6tGjhw4cOOARRi6//HJdfvnlPqsfAJobwgiater7tBwdPPbu3VtjvwsvvNAjjHTv3t39/LTTTlNycrL70aNHDyUnJysxMZGJpgDQCAgjCGhOp1Pffvuttm3bpoqKCl1//fUev580aZK2bNlS5/ts377d43VsbKy2bNmirl27MswCAD5GGEFAsCxLmzdvdvdyVPd0fPvtt+57snTt2rVGGElOTvYII61bt3b3bFT/TE5OVufOnWt8JouLAYB/EEbQ5P3P//yP7rnnHn399dcn3C8/P1/l5eWy2+3ubaNHj9agQYPc4SM+Pt7X5QIAvEQYQZNXWVlZaxCJjIxU9+7dPeZzWJblsc/gwYP9VSYAoIEII2hSvvrqK1VWVqpv377ubVdeeaW6deumhIQE/elPf3IPr5x22mkskw4AzQBhBMZZlqWPPvpIs2fP1qpVq3TppZd63DAuJCRE2dnZLBYGAM0U/6yEMeXl5Xr55ZfVp08fDR48WKtWrZIkrV69Wlu3bvXYlyACAM0XPSPwu/3792vBggWaO3euCgsLPX7XuXNnTZo0qdarWwAAzRNhBH5TWVmp22+/XUuWLNGRI0c8fpeamqq7775b11xzjcLC+LMEgGDC//XhN2FhYdqxY4c7iISEhOjf//3fNXny5Bp3sAUABA/mjMAnnE6n3nrrLblcLo/tmZmZioqK0uTJk/Xtt9/qjTfeIIgAQJCjZwSN6ueff9YLL7yg5557Tj/99JNWrlzpcdO4K664Qnv27FFMTIzBKgEATQk9I2gU33zzjSZOnKikpCT95S9/0U8//SRJmj17tsd+ISEhBBEAgAd6RtBglmVp/fr1mj17tt5//32P1U9tNpuuvfZaZWZmGqwQABAICCNokI0bN+rOO+9UTk6Ox/ZTTjlFN910k+666y516dLFUHUAgEBCGEGDREZGegSRDh066M4779TYsWMVFxdnsDIAQKAhjKBeXC6Xx31gzjrrLA0aNEjFxcW6++679ec//1nh4eEGKwQABCrCCE7o8OHDmjlzprKzs/Xhhx96BJJ33nlHMTExstlsBisEAAQ6rqbBca1atUq9evXSY489pqysLC1ZssTj97GxsQQRAMBJI4yghh9//FHXX3+9Lr/8cuXn50uSwsPDVVRUZLgyAEBzxDAN3CorK/X888/rgQceUElJiXt7Wlqa5s+fr549exqsDgDQXBFGIEn64osvNGHCBG3evNm9LT4+Xk8//bRGjhzJcAwAwGcII9CuXbuUmpqqqqoq97axY8dq1qxZOvXUUw1WBgAIBswZgU4//XSNGjVKktS7d29t2LBBCxcuJIgAAPyCnpEgtGvXLnXq1MnjMt0nn3xSvXv31sSJE1kvBADgV/SMBJEjR47o4YcfVnJysl588UWP38XHx2vSpEkEEQCA3xFGgsSHH36o3r17a8aMGXI6nbr//vu5VBcA0CQQRpq5n376STfeeKOGDBminTt3SpLCwsI0YcIEnXLKKYarAwCAOSPNVlVVlebPn69p06bJ4XC4tw8cOFDz58/XmWeeabA6AAB+Rxhphr788ktNmDDB4666p556qp5++mmNGjWKNUMAAE0KwzTN0OLFiz2CyM0336zt27dr9OjRBBEAQJNjsyzLMl1EXRwOh2JiYlRcXKzo6GjT5TR5Bw8eVHJyshISErRgwQINGDDAdEkAgCBU3/M3wzQBzul0qqqqSpGRke5tcXFxWrNmjbp168alugCAJo9hmgA3ZcoUpaSkKC8vz2P7mWeeSRABAAQEekYC2D//+U8988wzkqQBAwbou+++YxgLABBw6BkJUD/88INGjx7tfv3www8TRAAAAYkwEoCqqqo0fPhwHThwQJJ0zTXX6I477jBcFQAADUMYCUCPPvqo1q1bJ0lKSkrSSy+9xCW7AICARRgJMGvXrtWjjz4qSQoNDdVrr72m1q1bG64KAICGI4wEkH379mn48OFyuVySpBkzZujCCy80XBUAACeHMBIgLMvS6NGj9eOPP0qS/vjHP2rKlCmGqwIA4OQRRgKEZVk6//zzFRISojZt2ujVV19VaGio6bIAADhprDMSIEJCQvTggw/qoosuUkVFhdq1a2e6JAAAGgVhJMCkpaWZ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WeKrEpuNhhzryspK64ILLrBefPFFa9SoUYSRevD2OM+fP98644wzLKfT6a8SmwVvj/PEiROtSy65xGNbZmamNWDAAJ/W2ZzUJ4zcd9991plnnumxbejQodaQIUN8VlezHaZxOp3KyclRenq6e1tISIjS09OVnZ1da5vs7GyP/SVpyJAhx90fDTvOxyorK1NFRYVat27tqzKbhYYe60ceeUQJCQm6+eab/VFmwGvIcX7//feVmpqqiRMnKjExUb169dLMmTNVVVXlr7IDTkOO8wUXXKCcnBz3UE5+fr5WrlypK664wi81BwsT58KAuFFeQ+zfv19VVVVKTEz02J6YmKht27bV2qagoKDW/QsKCnxWZ6BryHE+1v3336/27dvX+OOHp4Yc6w0bNmjx4sXKzc31Q4XNQ0OOc35+vtasWaPhw4dr5cqV2rlzp2677TZVVFRo+vTp/ig74DTkOA8bNkz79+/XhRdeKMuyVFlZqQkTJugvf/mLP0oOGsc7FzocDh0+fFiRkZGN/pnNtmcEgeGJJ57QsmXL9O6776pFixamy2lWSkpKNGLECC1atEjx8fGmy2nWXC6XEhIStHDhQvXv319Dhw7VtGnTtGDBAtOlNStr167VzJkz9fzzz2vz5s165513tGLFCj366KOmS8NJarY9I/Hx8QoNDVVhYaHH9sLCQrVt27bWNm3btvVqfzTsOFd7+umn9cQTT+ijjz5Snz59fFlms+Dtsf7222/13XffKSMjw73N5XJJksLCwrR9+3Z16dLFt0UHoIb8Tbdr107h4eEKDQ11b+vRo4cKCgrkdDoVERHh05oDUUOO84MPPqgRI0bolltukST17t1bpaWlGjdunKZNm6aQEP593RiOdy6Mjo72Sa+I1Ix7RiIiItS/f39lZWW5t7lcLmVlZSk1NbXWNqmpqR77S9Lq1auPuz8adpwl6amnntKjjz6qVatW6ZxzzvFHqQHP22OdnJysrVu3Kjc31/24+uqrNWjQIOXm5iopKcmf5QeMhvxNDxgwQDt37nSHPUnasWOH2rVrRxA5joYc57KyshqBozoAWtxmrdEYORf6bGpsE7Bs2TLLbrdbr7zyivX1119b48aNs2JjY62CggLLsixrxIgR1pQpU9z7f/rpp1ZYWJj19NNPW3l5edb06dO5tLcevD3OTzzxhBUREWG99dZb1k8//eR+lJSUmPoKAcPbY30srqapH2+P8+7du62oqCjr9ttvt7Zv327985//tBISEqzHHnvM1FcICN4e5+nTp1tRUVHW66+/buXn51sffvih1aVLF+v666839RUCQklJibVlyxZry5YtliRr9uzZ1pYtW6zvv//esizLmjJlijVixAj3/tWX9t57771WXl6eNW/ePC7tPVnPPfecddppp1kRERHWeeedZ3322Wfu31100UXWqFGjPPZ/4403rG7dulkRERHWmWeeaa1YscLPFQcmb45zp06dLEk1HtOnT/d/4QHI27/poxFG6s/b47xx40YrJSXFstvt1hlnnGE9/vjjVmVlpZ+rDjzeHOeKigrr4Ycftrp06WK1aNHCSkpKsm677Tbr4MGD/i88gHz88ce1/j+3+tiOGjXKuuiii2q06devnxUREWGdccYZ1ssvv+zTGm2WRd8WAAAwp9nOGQEAAIGBMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMCo/wewP/2uHjsYCQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import roc_curve, auc\n",
    "roc_auc = auc(fpr,tpr)   # 准确率代表所有正确的占所有数据的比值\n",
    "plt.plot(fpr, tpr, 'k--', label='ROC (area = {0:.2f})'.format(roc_auc), lw=2)\n",
    "plt.savefig(\"旋转2的roc.png\",dpi=600)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
